/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
//package berkeley.project1;

/* Ocean.java */

/**
 *  The Ocean class defines an object that models an ocean full of sharks and
 *  fish.  Descriptions of the methods you must implement appear below.  They
 *  include a constructor of the form
 *
 *      public Ocean(int i, int j, int starveTime);
 *
 *  that creates an empty ocean having width i and height j, in which sharks
 *  starve after starveTime timesteps.
 *
 *  See the README file accompanying this project for additional details.
 */

public class Ocean {

  /**
   *  Do not rename these constants.  WARNING:  if you change the numbers, you
   *  will need to recompile Test4.java.  Failure to do so will give you a very
   *  hard-to-find bug.
   */

  public final static int EMPTY = 0;
  public final static int SHARK = 1;
  public final static int FISH = 2;

  /**
   *  Define any variables associated with an Ocean object here.  These
   *  variables MUST be private.
   */
  private int i, j, starveTime;
  private int content[][][];

  /**
   *  The following methods are required for Part I.
   */

  /**
   *  Ocean() is a constructor that creates an empty ocean having width i and
   *  height j, in which sharks starve after starveTime timesteps.
   *  @param i is the width of the ocean.
   *  @param j is the height of the ocean.
   *  @param starveTime is the number of timesteps sharks survive without food.
   */

  public Ocean(int i, int j, int starveTime) {
    this.i = i;
    this.j = j;
    this.content = new int[i][j][2];
    this.starveTime = starveTime;
  }
  
  public Ocean(int[][][] initContent, int starveTime) {
      this(initContent.length, initContent[0].length, starveTime);
      for (int x = 0; x < i; x++) {
          for (int y = 0; y < j; y++) {
              content[x][y][0] = initContent[x][y][0];
              content[x][y][1] = initContent[x][y][1];
          }
      }
  }

  /**
   *  width() returns the width of an Ocean object.
   *  @return the width of the ocean.
   */

  public int width() {
    // Replace the following line with your solution.
    return i;
  }

  /**
   *  height() returns the height of an Ocean object.
   *  @return the height of the ocean.
   */

  public int height() {
    // Replace the following line with your solution.
    return j;
  }

  /**
   *  starveTime() returns the number of timesteps sharks survive without food.
   *  @return the number of timesteps sharks survive without food.
   */

  public int starveTime() {
    // Replace the following line with your solution.
    return starveTime;
  }

  /**
   *  addFish() places a fish in cell (x, y) if the cell is empty.  If the
   *  cell is already occupied, leave the cell as it is.
   *  @param x is the x-coordinate of the cell to place a fish in.
   *  @param y is the y-coordinate of the cell to place a fish in.
   */

  public void addFish(int x, int y) {
    content[x][y][0] = FISH;
  }

  /**
   *  addShark() (with two parameters) places a newborn shark in cell (x, y) if
   *  the cell is empty.  A "newborn" shark is equivalent to a shark that has
   *  just eaten.  If the cell is already occupied, leave the cell as it is.
   *  @param x is the x-coordinate of the cell to place a shark in.
   *  @param y is the y-coordinate of the cell to place a shark in.
   */

  public void addShark(int x, int y) {
    content[x][y][0] = SHARK;
    content[x][y][1] = starveTime;
  }

  /**
   *  cellContents() returns EMPTY if cell (x, y) is empty, FISH if it contains
   *  a fish, and SHARK if it contains a shark.
   *  @param x is the x-coordinate of the cell whose contents are queried.
   *  @param y is the y-coordinate of the cell whose contents are queried.
   */

  public int cellContents(int x, int y) {
    return content[x][y][0];
  }

  /**
   *  timeStep() performs a simulation timestep as described in README.
   *  @return an ocean representing the elapse of one timestep.
   */

  public Ocean timeStep() {
    int[][][] newContent = new int[i][j][2];
    for (int x = 0; x < i; x++) {
        for (int y = 0; y < j; y++) {
            newContent[x][y][0] = newCellContent(x, y);
            newContent[x][y][1] = content[x][y][1];
        }
    }
    return new Ocean(newContent, starveTime);
  }
  
  private int newCellContent (int n, int m) {
      int sharkNeighbours = 0;
      int fishNeighbours = 0;
      
      for (int x = n - 1; x <= n + 1; x++) {
          for (int y = m - 1; y <= m + 1; y++) {
              if (x == n && y == m) {
                  continue;
              }
              int xCur = (x == -1) ? i - 1 : (x == i) ? 0 : x;
              int yCur = (y == -1) ? j - 1 : (y == j) ? 0 : y;
              if (content[xCur][yCur][0] == SHARK) {
                  sharkNeighbours++;
              } else if (content[xCur][yCur][0] == FISH) {
                  fishNeighbours++;
              }
          }
      }
      
      // if it was SHARK before and if it's not starving - return SHARK it dies otherwise
      if (content[n][m][0] == SHARK) {
          // shark eats fish 
          if (fishNeighbours >= 1) {
              content[n][m][1] = starveTime;
          } else {
              content[n][m][1]--; // shark gets hungrier
          }
          if (content[n][m][1] < 0) {
              content[n][m][1] = 0;
              return EMPTY;
          } else {
              return SHARK;
          }
      // is new SHARK can be born
      } else if ((sharkNeighbours >= 2) && 
              ((content[n][m][0] == FISH || (fishNeighbours >= 2)))) {
          content[n][m][1] = starveTime;
          return SHARK;
      } else if ((sharkNeighbours == 1) && (content[n][m][0] == FISH)) {
          return EMPTY;
      // is new FISH can be born
      } else if (fishNeighbours >= 2) {
          return FISH;
      // no changes
      } else {
          return content[n][m][0];
      }
  }

  /**
   *  The following method is required for Part II.
   */

  /**
   *  addShark() (with three parameters) places a shark in cell (x, y) if the
   *  cell is empty.  The shark's hunger is represented by the third parameter.
   *  If the cell is already occupied, leave the cell as it is.  You will need
   *  this method to help convert run-length encodings to Oceans.
   *  @param x is the x-coordinate of the cell to place a shark in.
   *  @param y is the y-coordinate of the cell to place a shark in.
   *  @param feeding is an integer that indicates the shark's hunger.  You may
   *         encode it any way you want; for instance, "feeding" may be the
   *         last timestep the shark was fed, or the amount of time that has
   *         passed since the shark was last fed, or the amount of time left
   *         before the shark will starve.  It's up to you, but be consistent.
   */

  public void addShark(int x, int y, int feeding) {
    content[x][y][0] = SHARK;
    content[x][y][1] = feeding;
  }

  /**
   *  The following method is required for Part III.
   */

  /**
   *  sharkFeeding() returns an integer that indicates the hunger of the shark
   *  in cell (x, y), using the same "feeding" representation as the parameter
   *  to addShark() described above.  If cell (x, y) does not contain a shark,
   *  then its return value is undefined--that is, anything you want.
   *  Normally, this method should not be called if cell (x, y) does not
   *  contain a shark.  You will need this method to help convert Oceans to
   *  run-length encodings.
   *  @param x is the x-coordinate of the cell whose contents are queried.
   *  @param y is the y-coordinate of the cell whose contents are queried.
   */

  public int sharkFeeding(int x, int y) {
    return content[x][y][1];
  }

}
